<?php
//**************************************************//
//                                                  //
//                 Gagatemplate 4.2                 //
//                   Octobre 2008                   //
//                                                  //
//    Email : gaga26@gmail.com                      //
//      Web : www.gagatemplate.com                  //
//  Licence : GNU LGPL                              //
//                                                  //
//      Have fun                                    //
//                                  Gaga26          //
//**************************************************//

class gagatemplate
{
	// les balises
		public $bal = array(
			'var'		=> array('{', '}', '$'),							// vars
			'include'	=> array('include', 'file', 'cache'),				// include
			'cond'		=> array('if', 'elseif', 'else', 'cond'),			// condition
			'foreach'	=> array('foreach', 'var', 'as', 'foreachelse'),	// boucle
			'func'		=> array('function', 'name'),						// fonction
			'com'		=> array('/#', '#/'));								// commentaires

	// les dossiers
		public $template_dir = '';	// dossier des fichiers templates
		public $compile_dir = 'cache/tpl_c/';	// dossier des fichiers compilés
		public $cache_dir = 'cache/';			// dossier des fichiers mis en cache
		private $this_dir;					    // dossier actuel (root)

	// var pour les blocks et vars
		public $vars = array();					// tableau contenant les variables

	// var pour le cache
		public $cache = false;					// activation de la mise en cache
		public $cache_time = 0;					// temps du cache en secondes
		public $compile = false;                // Forcer la compilation systmatique

	// var pour le temps d'execution
		private $temps_tpl = array();			// temps de création par template
		private $one_cache = false;
	
	// infos
		private $infos = array();
		private $infos_new = false;
		private $infos_dir= false;

	// message d'erreur
		public $error_display = true;			// activer l'affichage des erreurs
		private $error_msg = '<br /><b>GagaTemplate error :</b><br />'; // le début des messages d'erreur
		public $error = false;
	
	public function __construct($dir = '', $i='cache/') {
		$this->root = addslashes($dir);
		$this->infos_dir = $i;
		$this->get_infos();
	}
	
	public function assign($nom, $valeur = NULL){
		if(is_array($nom))
			$this->vars = array_merge($this->vars, $nom);
		else
			$this->vars[$nom] = $valeur;
	}
	
	public function assign_array($nom, $a) {
		if(isset($this->vars[$nom]) && !is_array($this->vars[$nom]))
			$this->error('Vous &eacute;crasez une variable par un array');
		krsort($a);
		
		if(strpos($nom, '.')) {
			$e = explode('.', $nom);
			$b = '$this->vars';
			$c = count($e) -1;
			for ($i=0 ; $i<$c ; $i++) {
				$b .= '[\'' . $e[$i] . '\']';
				eval('$c_b_p = count(' . $b . ') - 1;');
				$b .= '[' . $c_b_p . ']';
			}
			$b .= '[\'' . $e[$c] . '\'][] = $a;';
			eval($b);
		}else{
			$this->vars[$nom][] = $a;
		}
	}
	
	public function is_in_cache($f, $s=false) {
		$s = $s ? $s : 'base';
		$f_tpl = $this->root.$this->template_dir.$f;
		if($this->cache && !$this->compile) {
			if(isset($this->infos[$f_tpl][$s]['cache'])){
				$d = time()-$this->infos[$f_tpl][$s]['cache']-$this->cache_time;
				return ($this->cache_time == 0 || $d < 0);
			}
		}
		return false;
	}
	
	public function parse($file, $surnom = false, $disp = true) {
		$d = microtime(true);
		$f_tpl = $this->root.$this->template_dir.$file;

		$r = $this->cache ? $this->parse_cache_file($file, $f_tpl, $surnom, $disp) : $r = $this->parse_compile_file($file, $f_tpl, $disp);
			
		if($this->infos_new)
			$this->set_infos();
		
		$this->temps_tpl[$file] = microtime(true)-$d;
		
		return $r;
	}
	
	private function parse_compile_file($file, $f_tpl, $disp=true) {
		$rp = false;
		if(isset($this->infos[$f_tpl]['base']['compile_file'])){
			$nc = $this->infos[$f_tpl]['base']['compile_file'];
			if(filemtime($f_tpl) > $this->infos[$f_tpl]['base']['compile']) $rp=true;
		}else $nc = $this->root.$this->compile_dir.$this->name($file).'.g.php';
		
		if(!is_file($nc) || $rp || $this->compile){
			$nc = $this->parse_file($file, $f_tpl);
		
			$this->infos_new = true;
			$this->infos[$f_tpl]['base']['compile'] = time();
			$this->infos[$f_tpl]['base']['compile_file'] = $nc;
		}
				
		if($disp) {
			foreach($this->vars as $k=>$v)
				${$k} = $v;
			include($nc);
		}
		
		return $nc;
	}
	
	private function parse_cache_file($file, $f_tpl, $surnom, $disp=true) {
		$s = $surnom ? $surnom : 'base';
		if($this->is_in_cache($file, $surnom)){
			$r = $this->infos[$f_tpl][$s]['cache_file'];
		}else{
			ob_start();
			$this->parse_compile_file($file, $f_tpl, true);
			$c = ob_get_contents();
			ob_end_clean();
			
			$n = $surnom ? '-'.$surnom : '';
			$n_c = $this->root.$this->cache_dir.$this->name($file).$n.'.g.txt';
			@unlink($n_c);
			if(!file_put_contents($n_c, $c))
				$this->error('Le <strong>dossier des fichiers mis en cache</strong> n\'est pas ouvert en &eacute;criture !', true);
			$this->infos_new = true;
			$this->infos[$f_tpl][$s]['cache'] = time();
			$this->infos[$f_tpl][$s]['cache_file'] = $n_c;
			$r = $n_c;
		}	
		if($disp)
			include($r);
		$this->one_cache = true;
		return $r;
	}
	
	protected function parse_file($file, $f_tpl){
		if(!is_file($f_tpl))
			$this->error('Le fichier <i>'.$f_tpl.'</i> n\'existe pas !', true);
		$c = file_get_contents($f_tpl);

		$c = preg_replace('`'.preg_quote($this->bal['com'][0]).'(.+)'.preg_quote($this->bal['com'][1]).'`isU', '', $c);

		$c = $this->parse_include($c);
		$c = $this->parse_vars($c);
		$c = $this->parse_array($c);
		$c = $this->parse_condition($c);
		$c = $this->parse_function($c);

		$n_c = $this->root.$this->compile_dir.$this->name($file).'.g.php';
		
		@unlink($n_c);
		if(!file_put_contents($n_c, $c))
			$this->error('Le <strong>dossier des fichiers compil&eacute;s</strong> n\'est pas ouvert en &eacute;criture !', true);
		

		return $n_c;
	}
	
	protected function parse_vars(&$c) {
		$c = preg_replace('`\[(\S+)\]`U', '[\'\1\']', $c);
		$c = preg_replace('`'.preg_quote($this->bal['var'][0]).'(\S+)'.preg_quote($this->bal['var'][1]).'`isU', '<?php echo $\1; ?>', $c);
		return $c;
	}
	
	protected function parse_array(&$c) {
		while(strpos($c, '</'.preg_quote($this->bal['foreach'][0])) !== FALSE)
			$c = preg_replace('`<('.preg_quote($this->bal['foreach'][0]).')\s*'.preg_quote($this->bal['foreach'][1]).'="(.+)"\s*'.preg_quote($this->bal['foreach'][2]).'="(.+)"\s*>((?:.(?!<'.preg_quote($this->bal['foreach'][0]).'>))*)</'.preg_quote($this->bal['foreach'][0]).'>`sU', '<?php if(isset(\2)){ foreach(\2 as $\3) { ?>\4 <?php }} ?>', $c);
		$c = str_replace('<'.$this->bal['foreach'][3].' />', '<?php }} else { if (true) {?>', $c);
		return $c;
	}
	
	protected function parse_include(&$c) {
		$c = preg_replace_callback('`<'.preg_quote($this->bal['include'][0]).' '.preg_quote($this->bal['include'][1]).'="(.+)"\s?('.preg_quote($this->bal['include'][2]).'="on")?\s?/?>`isU', array('gagatemplate', 'parse_include_callback'), $c);
		
		return $c;
	}
	
	private static function parse_include_callback($m){
		if(strpos($m[1], '$')==1) {//var
			$v = substr($m[1], 1);
			if(!isset($this->vars[$v])) {
				$this->error('Variable pour l\'include non d&eacute;clar&eacute;e !');
				return false;
			}
			$f = $this->vars[$v];
		}else
			$f = $m[1];
		
		return isset($m[2]) ? '<?php $gaga_cache=$this->cache; $this->cache=true; $this->parse("'.str_replace('\\','\\\\',$f).'"); $this->cache=$gaga_cache; ?>' : '<?php $this->parse("'.str_replace('\\','\\\\',$f).'"); ?>';
	}
	
	protected function parse_condition(&$c) {
		return preg_replace(array(
				'`<'.preg_quote($this->bal['cond'][0]).' '.preg_quote($this->bal['cond'][3]).'="(.+)">`sU',
				'`</'.preg_quote($this->bal['cond'][0]).'>`sU',
				'`<'.preg_quote($this->bal['cond'][1]).' '.preg_quote($this->bal['cond'][3]).'="(.+)"\s?/?>`sU',
				'`<'.preg_quote($this->bal['cond'][2]).'\s?/?>`sU',
			),array(
				'<?php if(\1) { ?>', 
				'<?php } ?>',
				'<?php }elseif(\1){ ?>', 
				'<?php }else{ ?>'
			),
			$c);
	}
	
	protected function parse_function(&$c){
		return preg_replace_callback('`<'.preg_quote($this->bal['func'][0]).' '.preg_quote($this->bal['func'][1]).'="(\w+)" (.*)?/?>`isU', array('gagatemplate', 'parse_function_callback'), $c); 
	}
	
	private static function parse_function_callback($m){
		$args = '';
		foreach(explode(' ', $m[2]) as $v){
			if(!empty($v)){
				$arg = substr($v, strpos($v, '"'));
				$args .= substr($v, 0, strpos($v, '"')) == 'string=' ? $arg.', ' : substr($arg, 1, strlen($arg)-2).', ';
			}
		}
		$args = substr($args, 0, strlen($args)-2);
		if(!function_exists($m[1])) exit('La fonction <b>'.$m[1].'()</b> n\'existe pas !!!');
		return '<?php echo '.$m[1].'('.$args.'); ?>';
	}
	
	protected function name($f){
		return str_replace('/', '-', substr($f, 0, strrpos($f, '.')));
	}

	protected function error($texte, $exit = false) {
		if ($this->error_display) {
			echo $this->error_msg.$texte.'<br /><br />';
			if ($exit)	exit();
		}
		$this->error = true;
	}
	
	public function del_cache($f, $s='base') {
		$f_tpl = $this->root.$this->template_dir.$f;
		if($s === true){
			foreach($this->infos[$f_tpl] as $k=>$v){
				@unlink($this->infos[$f_tpl][$k]['cache_file']);
				unset($this->infos[$f_tpl][$k]);
			}
		}elseif(isset($this->infos[$f_tpl][$s])) {
			@unlink($this->infos[$f_tpl][$s]['cache_file']);
			unset($this->infos[$f_tpl][$s]['cache']);
			unset($this->infos[$f_tpl][$s]['cache_file']);
		}
		$this->set_infos();
	}

	private function clean($dir,$ext=false) {
		$l = $this->root.$dir;
		$a = array();
		$d = opendir($l);
		while ($f = readdir($d)) {
			if ($f != '.' && $f != '..') {
				if (($ext == false) || strpos($f, $ext)) {
					$a[] = $f;
					@unlink($l.$f);
				}
			}
		}
		closedir($d);
		return $a;
	}

	public function clean_compile_dir() {
		$this->infos = array();
		$this->set_infos();
		return $this->clean($this->compile_dir, '.g.php');
	}

	public function clean_cache_dir() {
		foreach($this->infos as $k => $v){
			foreach($v as $sk=>$sv){
				unset($this->infos[$k][$sk]['cache']);
				unset($this->infos[$k][$sk]['cache_file']);
			}
		}
		$this->set_infos();
		return $this->clean($this->cache_dir, '.g.txt');
	}

	public function debug($url, $dec) {
		include($this->root.'libs/debug.php');
	}
	
	private function get_infos() {
		$f = $this->root.$this->infos_dir.'infos.gagatpl';
		if(is_file($f))
			$this->infos = unserialize(file_get_contents($f));
	}
	
	private function set_infos() {
		$f = $this->root.$this->infos_dir.'infos.gagatpl';
		@unlink($f);
		file_put_contents($f, serialize($this->infos));
	}
}
?>